package scene;

import raytracer.IntersectionResult;
import raytracer.Ray;
import raytracer.Vector3f;

/// A class representing a sphere.
public class Sphere extends Shape {

	// / Create a default white sphere centered at the origin with a radius of
	// 1.
	public Sphere() {
		pos = new Vector3f();
		radius = 1.0f;
		collisionRadius = radius;
		radiusSquared = radius * radius;
		material = new Material();
	}

	// / Create a sphere with the specified radius, position, and color.
	public Sphere(float newRadius, float x, float y, float z, Material newMaterial) {
		if (newMaterial == null)
			throw new NullPointerException();

		radius = newRadius;
		radiusSquared = radius * radius;
		pos = new Vector3f(x, y, z);
		material = newMaterial;
		collisionRadius = radius;
	}

	public IntersectionResult intersect(Ray ray) {
		//Profiler.start("intersect" + Thread.currentThread().getName());
		
		Vector3f d = ray;
		Vector3f c = this.pos;
		Vector3f v = ray.start.subtract(c);

		// discriminant = (v dot d)^2 - (v^2 - r^2)
		float vdotd = v.dot(d);
		float discriminant = (vdotd * vdotd - (v.dot(v) - this.radiusSquared));

		if (discriminant >= 0) {
			// solutions: -(v dot d) +- sqrt(discriminant)

			float sqrt = (float) Math.sqrt(discriminant);
			// Might be irrelevant since squares are always on the same side of the camera
			float sol1 = -vdotd + sqrt;
			float sol2 = -vdotd - sqrt;

			if (sol1 > 0 && sol2 > 0) {
				float dist = (sol1 < sol2 ? sol1 : sol2);
				//float dist = sol2;	
				Vector3f p = ray.start.add(d.scale(sol2));
				Vector3f normal = p.subtract(c).normalizeLocal();
				IntersectionResult result = new IntersectionResult(p, normal, this.material, dist, this);
				return result;
			}
		}
		return null;
	}

	@Override
	public String toString() {
		return "Sph" + id + " @" + this.pos + " r" + this.radius + " mat: " + this.material;
	}

	// / The radius of the sphere.
	public float radiusSquared;
	public float radius;

	// / The material properies of the sphere.
	public Material material;

	public String getName() {
		return "Sph" + id;
	}

}
